﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using ABFramework;
using System.IO;
using UIFramework;

namespace LuaFramework
{
    /// <summary>
    /// Lua帮助类-管理虚拟机已经自定义加载路径等
    /// </summary>
    public class LuaHelper : MonoBehaviour
    {
        //Lua虚拟机
        LuaEnv _Env = new LuaEnv();
        //缓存lua文件的字典
        Dictionary<string, byte[]> _DicLuaScripts = new Dictionary<string, byte[]>();

        #region 实现LuaHpeler类的单例
        private static LuaHelper _instance;
        private LuaHelper()
        {
            _Env.AddLoader(CustomLoader);

        }
        public static LuaHelper Instance
        {
            get
            {
                if (_instance == null)
                {
                    GameObject go = new GameObject();
                    _instance = go.AddComponent<LuaHelper>();
                    go.name = "LuaFrameHelper";
                    go.transform.parent = GameInit.GameManager.transform;
                }
                return _instance;
            }
        }
        #endregion

        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            //Debug.Log("LuaHelper Init!");
            _Env.DoString("util =  require('Tools/util')");
            //执行Lua热修复脚本
            _Env.DoString("require('Hotfix')");
        }

        /// <summary>
        /// 自定义lua文件加载路径，并添加缓存
        /// </summary>
        /// <param name="filepath">lua文件相对路径</param>
        /// <returns></returns>
        private byte[] CustomLoader(ref string filepath)
        {
            //加载资源的根目录
            string root_path = PathTools.GetPlatformABRootPath() + "/" + PathTools.LUA_DESTION_PATH; ;
#if UNITY_EDITOR
            //在编辑器模式下且未开启热更新则直接在本地读取,而不是从资源下载路径读取
            if (!GameInit.Instance._EnabledHotUpdate || GameInit.Instance._OnlyReadLocalLuaScript)
            {
                root_path = Application.dataPath + "/" + PathTools.LUA_ROOT_PATH;
            }
#endif
            //查找缓存中是否有需要加载的文件
            if (_DicLuaScripts.ContainsKey(filepath))
            {
                return _DicLuaScripts[filepath];
            }
            //加载lua文件的完整路径
            string full_name = root_path + "/" + filepath + ".lua";

            //检测文件是否存在
            if (!File.Exists(full_name))
            {
                Debug.LogError("需要加载的Lua文件不存在!!!" + full_name);
                return null;
            }
            //读取到byte数组
            byte[] file_bytes = File.ReadAllBytes(full_name);
            //加入到缓存，下次读取直接从缓存里取
            _DicLuaScripts.Add(filepath, file_bytes);

            return file_bytes;
        }

        /// <summary>
        /// 执行代码
        /// </summary>
        /// <param name="str"></param>
        public void DoString(string str)
        {
            _Env.DoString(str, "chunk", null);
        }

        /// <summary>
        /// 调用lua脚本的方法
        /// </summary>
        /// <param name="script_name"></param>
        /// <param name="func_name"></param>
        /// <param name="args"></param>
        public void CallLuaFunction(string script_name, string func_name, params object[] args)
        {
            LuaTable table = _Env.Global.Get<LuaTable>(script_name);
            LuaFunction func = table.Get<LuaFunction>(func_name);
            func.Call(args);
        }
        /// <summary>
        /// 打印缓存的lua脚本文件名
        /// </summary>
        public void PrintCacheFileList()
        {
            string str = "缓存的Lua文件共【" + (_DicLuaScripts.Count) + "】个";
            int count = 0;
            foreach (var item in _DicLuaScripts.Keys)
            {
                count++;
                str += "\n" + count + "." + item;
            }
            Debug.Log(str);
        }

        /// <summary>
        /// 获取虚拟机实例
        /// </summary>
        /// <returns></returns>
        public LuaEnv GetLuaEnv()
        {
            if (_Env == null)
            {
                Debug.LogError("没有虚拟机实例？？？");
                return null;
            }
            return _Env;
        }
        /// <summary>
        /// 给指定游戏对象添加生命周期组件
        /// </summary>
        /// <param name="go">需要添加的物体</param>
        public void AddMapLifeCycleComponent(GameObject go)
        {
            if (go.GetComponent<MapLifeCycle>() == null)
            {
                go.AddComponent<MapLifeCycle>();
            }
        }

        /// <summary>
        /// 销毁时自动时释放资源
        /// </summary>
        private void OnDestroy()
        {
            //释放虚拟机
            DisposeEnv();
        }

        /// <summary>
        /// 主动释放虚拟机
        /// </summary>
        public void DisposeEnv()
        {
            #region 引用信息（Lua或c#持有的委托）
            //Debug.Log(@"解决方案
            //释放这些delegate即可，所谓释放，在C#中，就是没有引用：
            //你是在C#通过LuaTable.Get获取并保存到对象成员，赋值该成员为null；
            //你是在lua那把lua函数注册到一些事件事件回调，反注册这些回调；
            //如果你是通过xlua.hotfix(class, method, func)注入到C#，则通过xlua.hotfix(class, method, nil)删除；
            //要注意以上操作在Dispose之前完成。");

            //_Env.DoString("util.print_func_ref_by_csharp()");

            //删除生命周期的引用
            MapLifeCycle.HandDisposeAll();
            //移除UIMsg的引用
            UIMsgManager.Instance.RemoveAllListener();
            //移除Tips的引用
            TipsManager.Instance.RemoveAllListener();
            //移除热修复持有的引用[Hitfix.lua UnHotfix()]
            DoString("UnHotfix()");
            //释放所有C#监听事件对lua的引用
            DoString("Tool:ClearAllCSReference()");
            #endregion

            _DicLuaScripts.Clear();
            _DicLuaScripts = null;

            //释放虚拟机
            _Env.Dispose();
        }

    }

}